package com.tencent.sr.iris.activity.service.activity.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.PageHelper;
import com.google.common.util.concurrent.RateLimiter;
import com.tencent.sr.iris.activity.common.config.DependencyConfig;
import com.tencent.sr.iris.activity.common.util.ShardingIdUtil;
import com.tencent.sr.iris.activity.domain.event.dto.ActivityTaskDTO;
import com.tencent.sr.iris.activity.interfaces.dto.AwardInfoDTO;
import com.tencent.sr.iris.activity.interfaces.dto.MultiTaskActivityDTO;
import com.tencent.sr.iris.activity.interfaces.enums.ActivityIdEnum;
import com.tencent.sr.iris.activity.interfaces.request.IrisPaginationRequest;
import com.tencent.sr.iris.activity.interfaces.request.acticity.FreshActivityRecordRequest;
import com.tencent.sr.iris.activity.repository.entity.TIrisUserTaskRecordDO;
import com.tencent.sr.iris.activity.repository.mapper.TIrisUserTaskRecordMapper;
import com.tencent.sr.iris.activity.repository.mapper.example.TIrisUserTaskRecordExample;
import com.tencent.sr.iris.activity.repository.singlemapper.example.TIrisUserTaskBatchDataExample;
import com.tencent.sr.iris.activity.service.activity.ActivityCommonService;
import com.tencent.sr.iris.activity.service.activity.IrisActivityTaskService;
import com.tencent.sr.iris.activity.service.activity.IrisUserActivityRecordService;
import com.tencent.sr.iris.activity.service.config.ActivityConfig;
import com.tencent.sr.iris.activity.service.event.util.ActivityCommonUtilService;
import com.tencent.sr.rmall.material.interfaces.enums.EYesNo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;

@Slf4j
@Service
public class DataRefreshService {

    @Resource
    private TIrisUserTaskRecordMapper taskRecordMapper;

    @Resource
    private ActivityConfig activityConfig;

    @Resource
    private DependencyConfig dependencyConfig;

    @Resource
    private IrisUserActivityRecordService activityRecordService;

    @Resource
    private ActivityCommonUtilService activityCommonUtilService;

    @Resource
    private IrisActivityTaskService irisActivityTaskService;

    @Resource
    private ActivityCommonService activityCommonService;

    private RateLimiter FRESH_DATA_RATELIMITER = RateLimiter.create(20);

    public void addOrUpdateActivityRecords(IrisPaginationRequest<FreshActivityRecordRequest> request) {
        long millis = System.currentTimeMillis();
        FreshActivityRecordRequest queryCriteria = request.getQueryCriteria();
        boolean singleShard = queryCriteria.getShardingId() != null;
        AtomicLong total = new AtomicLong(0);
        AtomicLong error = new AtomicLong(0);
        if (singleShard) {
            queryCriteria.setSearchAfter(null);
            shardingSearch(request, total, error);
            return;
        }
        for (long i = 0; i < ShardingIdUtil.SHARDING_NUM; i++) {
            try {
                queryCriteria.setShardingId((int) i);
                queryCriteria.setSearchAfter(null);
                shardingSearch(request, total, error);
            } catch (Exception e) {
                log.error("活动记录刷数：shard刷数错误，共处理{}条活动记录，错误{}条", total.get(), error.get());
            }
        }
        log.info("活动记录刷数：刷数完成，共处理{}条活动记录，错误{}条，耗时{}毫秒", total.get(), error.get(), System.currentTimeMillis() - millis);
    }

    private void shardingSearch(IrisPaginationRequest<FreshActivityRecordRequest> request,
                                AtomicLong total, AtomicLong error) {
        List<TIrisUserTaskRecordDO> response = doSearch(request);
        String searchAfter;
        FreshActivityRecordRequest queryCriteria = request.getQueryCriteria();
        long millis = System.currentTimeMillis();
        AtomicInteger success = new AtomicInteger();
        DependencyConfig.Config freshData = dependencyConfig.getConfigs().get("freshData");
        while (CollectionUtil.isNotEmpty(response)) {

            Map<String, MultiTaskActivityDTO> multiTaskActivityConfig = activityConfig.getMultiTaskActivityConfig();
            Map<String, Map<String, List<TIrisUserTaskRecordDO>>> map = activityCommonUtilService.groupBy(response, multiTaskActivityConfig);
            map.values().forEach(i ->
                    i.values().forEach(taskRecords -> {
                        try {
                            if (freshData != null) {
                                FRESH_DATA_RATELIMITER.setRate(freshData.getPermitsPerSecond());
                                FRESH_DATA_RATELIMITER.acquire();
                                log.info("活动记录刷数，当前限流{}qps", FRESH_DATA_RATELIMITER.getRate());
                            }
                            if (queryCriteria.getUpdateTime() == null) {
                                TIrisUserTaskRecordDO taskRecordDO = taskRecords.get(taskRecords.size() - 1);
                                activityRecordService.doAddOrUpdateActivityRecord(taskRecordDO.getUid(), taskRecordDO.getActivityId(),
                                        taskRecordDO.getTaskPeriod(), taskRecords);
                            } else {
                                TIrisUserTaskRecordDO first = taskRecords.get(0);
                                activityRecordService.addOrUpdateActivityRecord(first.getUid(), first.getCurrentTaskStartTime().getTime(),
                                        first.getActivityId(), first.getPeriodType(), first.getTaskPeriod());
                            }
                            success.getAndIncrement();
                            total.getAndIncrement();
                        } catch (Exception e) {
                            log.error("活动记录刷数：活动记录刷数错误，已处理总数：{}，错误数：{}，shardingId：{}，", total.get(), error.getAndIncrement(), queryCriteria.getShardingId(), e);
                        }
                    })
            );
            searchAfter = response.stream().max(Comparator.comparing(TIrisUserTaskRecordDO::getUid))
                    .map(TIrisUserTaskRecordDO::getUid).orElse(null);
            queryCriteria.setSearchAfter(searchAfter);

            response = doSearch(request);
        }

        log.info("活动记录刷数：处理{}条任务记录，耗时{}毫秒，shardingId：{}，activityId：{}",success.get(),
                System.currentTimeMillis() - millis, queryCriteria.getShardingId(), queryCriteria.getActivityId().getCode());
    }

    /**
     * 多阶段任务搜索
     *
     * @param request
     * @return
     */
    private List<TIrisUserTaskRecordDO> doSearch(IrisPaginationRequest<FreshActivityRecordRequest> request) {

        FreshActivityRecordRequest queryCriteria = request.getQueryCriteria();
        String searchAfter = queryCriteria.getSearchAfter();
        boolean bSearchAfter = StrUtil.isNotBlank(searchAfter);

        Integer pageSize = request.getPageSize();
        Integer pageNum = bSearchAfter ? 1 : request.getPageNum();

        QueryWrapper<TIrisUserTaskRecordDO> query = new QueryWrapper<>();
        LambdaQueryWrapper<TIrisUserTaskRecordDO> wrapper = query
                .select(" DISTINCT " + TIrisUserTaskBatchDataExample.UID).lambda()
                .orderByAsc(TIrisUserTaskRecordDO::getUid);

        if (bSearchAfter) {
            wrapper.gt(TIrisUserTaskRecordDO::getUid, searchAfter);
        }

        buildCondition(wrapper, request, null);
        if (queryCriteria.getUpdateTime() != null) {
            wrapper.ge(TIrisUserTaskRecordDO::getUpdateTime, DateUtil.date(queryCriteria.getUpdateTime()));
        }

        //先根据uid去重分页查询
        PageHelper.startPage(pageNum, pageSize, false);
        List<TIrisUserTaskRecordDO> list = taskRecordMapper.selectList(wrapper);
        if (CollectionUtil.isEmpty(list)) {
            return list;
        }
        List<String> uidList = list.stream().map(TIrisUserTaskRecordDO::getUid).collect(Collectors.toList());

        //查询uid下的相同条件的所有任务
        wrapper = Wrappers.lambdaQuery(TIrisUserTaskRecordDO.class);
        buildCondition(wrapper, request, uidList);
        return taskRecordMapper.selectList(wrapper);
    }

    /**
     * 构建条件
     *
     * @param wrapper
     * @param request
     * @param uidList
     */
    private void buildCondition(LambdaQueryWrapper<TIrisUserTaskRecordDO> wrapper,
            IrisPaginationRequest<FreshActivityRecordRequest> request, List<String> uidList) {
        FreshActivityRecordRequest queryCriteria = request.getQueryCriteria();
        String activityType = queryCriteria.getActivityType().getCode();

        wrapper.setEntityClass(TIrisUserTaskRecordDO.class)
                .eq(TIrisUserTaskRecordDO::getShardingId, queryCriteria.getShardingId())
                .eq(TIrisUserTaskRecordDO::getActivityType, activityType)
                .eq(TIrisUserTaskRecordDO::getStatus, EYesNo.NO.name());

        ActivityIdEnum activityIdEnum = queryCriteria.getActivityId();
        if (activityIdEnum != null) {
            wrapper.eq(TIrisUserTaskRecordDO::getActivityId, activityIdEnum.getCode());
        }

        Long startTime = queryCriteria.getStartTime();
        Long endTime = queryCriteria.getEndTime();
        if (startTime != null && endTime != null) {
            Date start = DateUtil.beginOfMonth(DateUtil.date(startTime));
            Date end = DateUtil.endOfMonth(DateUtil.date(endTime));
            wrapper.between(TIrisUserTaskRecordDO::getCurrentTaskStartTime, start, end);
        }

        //uidList1
        List<String> uidList1 = queryCriteria.getUidList();
        if (CollectionUtil.isNotEmpty(uidList1)) {
            wrapper.in(TIrisUserTaskRecordDO::getUid, uidList1);
        }

        if (CollectionUtil.isNotEmpty(uidList)) {
            wrapper.in(TIrisUserTaskRecordDO::getUid, uidList);
        }
    }

    public void refreshSendAward(IrisPaginationRequest<FreshActivityRecordRequest> request) {
        long millis = System.currentTimeMillis();
        FreshActivityRecordRequest queryCriteria = request.getQueryCriteria();
        boolean singleShard = queryCriteria.getShardingId() != null;
        AtomicLong total = new AtomicLong(0);
        AtomicLong error = new AtomicLong(0);
        if (singleShard) {
            queryCriteria.setSearchAfterTaskRecordId(null);
            shardingRefreshSendAward(request, total, error);
            return;
        }
        for (long i = 0; i < ShardingIdUtil.SHARDING_NUM; i++) {
            try {
                queryCriteria.setShardingId((int) i);
                queryCriteria.setSearchAfterTaskRecordId(null);
                shardingRefreshSendAward(request, total, error);
            } catch (Exception e) {
                log.error("应发奖励金刷数：shard刷数错误，共处理{}条活动记录，错误{}条", total.get(), error.get());
            }
        }
        log.info("应发奖励金刷数：刷数完成，共处理{}条活动记录，错误{}条，耗时{}毫秒", total.get(), error.get(), System.currentTimeMillis() - millis);
    }

    private void shardingRefreshSendAward(IrisPaginationRequest<FreshActivityRecordRequest> request, AtomicLong total, AtomicLong error) {
        List<TIrisUserTaskRecordDO> response = doSearchSendAward(request);
        String searchAfter;
        FreshActivityRecordRequest queryCriteria = request.getQueryCriteria();
        long millis = System.currentTimeMillis();
        AtomicInteger success = new AtomicInteger();
        DependencyConfig.Config freshData = dependencyConfig.getConfigs().get("freshData");
        if (freshData != null) {
            FRESH_DATA_RATELIMITER.setRate(freshData.getPermitsPerSecond());
            FRESH_DATA_RATELIMITER.acquire();
            log.info("应发奖励金刷数，当前限流{}qps", FRESH_DATA_RATELIMITER.getRate());
        }

        Integer shardingId = queryCriteria.getShardingId();
        while (CollectionUtil.isNotEmpty(response)) {
            total.addAndGet(response.size());
            Map<String, List<TIrisUserTaskRecordDO>> map = response.stream().collect(Collectors.groupingBy(TIrisUserTaskRecordDO::getTaskId));
            map.forEach((taskId, taskRecords) -> {
                ActivityTaskDTO task = irisActivityTaskService.getTaskByTaskBizId(taskId);
                if (task == null) {
                    error.incrementAndGet();
                    log.error("刷新应发奖励金查询不到任务信息，taskId：{}", taskId);
                    return;
                }
                AwardInfoDTO awardInfo = activityCommonService.getAwardInfo(task.getAwardInfo());
                Integer value = Optional.ofNullable(awardInfo).map(AwardInfoDTO::getValue).orElse(0);
                if (value == 0) {
                    log.error("刷新应发奖励金金额为0，taskId：{}", taskId);
                }

                List<String> recordBizIdList = taskRecords.stream().map(TIrisUserTaskRecordDO::getBizId).collect(Collectors.toList());
                TIrisUserTaskRecordExample example = new TIrisUserTaskRecordExample();
                example.createCriteria()
                        .andPropertyEqualTo(TIrisUserTaskRecordExample.SHARDING_ID, shardingId)
                        .andPropertyEqualTo(TIrisUserTaskRecordExample.STATUS, EYesNo.NO.getValue())
                        .andPropertyEqualTo(TIrisUserTaskRecordExample.TASK_ID, taskId)
                        .andPropertyIn(TIrisUserTaskRecordExample.BIZ_ID, recordBizIdList);
                TIrisUserTaskRecordDO update = new TIrisUserTaskRecordDO();
                update.setSendAward(value);

                int i = taskRecordMapper.updateByExampleSelective(update, example);
                success.addAndGet(i);
            });

            searchAfter = response.stream().max(Comparator.comparing(TIrisUserTaskRecordDO::getBizId))
                            .map(TIrisUserTaskRecordDO::getBizId).orElse(null);
            queryCriteria.setSearchAfterTaskRecordId(searchAfter);

            response = doSearchSendAward(request);
        }

        log.info("应发奖励金刷数：处理{}条任务记录，耗时{}毫秒，shardingId：{}，activityId：{}",success.get(),
                System.currentTimeMillis() - millis, queryCriteria.getShardingId(), queryCriteria.getActivityId().getCode());
    }

    private List<TIrisUserTaskRecordDO> doSearchSendAward(IrisPaginationRequest<FreshActivityRecordRequest> request) {
        Integer pageSize = request.getPageSize();
        FreshActivityRecordRequest queryCriteria = request.getQueryCriteria();

        String searchAfter = queryCriteria.getSearchAfterTaskRecordId();
        boolean bSearchAfter = StrUtil.isNotBlank(searchAfter);

        Integer pageNum = bSearchAfter ? 1 : request.getPageNum();

        QueryWrapper<TIrisUserTaskRecordDO> query = new QueryWrapper<>();
        LambdaQueryWrapper<TIrisUserTaskRecordDO> wrapper = query.lambda().orderByAsc(TIrisUserTaskRecordDO::getBizId);

        if (bSearchAfter) {
            wrapper.gt(TIrisUserTaskRecordDO::getBizId, searchAfter);
        }

        buildCondition(wrapper, request, null);
        if (queryCriteria.getUpdateTime() != null) {
            wrapper.ge(TIrisUserTaskRecordDO::getUpdateTime, DateUtil.date(queryCriteria.getUpdateTime()));
        }
        wrapper.eq(TIrisUserTaskRecordDO::getSendAward, 0);

        PageHelper.startPage(pageNum, pageSize, false);
        return taskRecordMapper.selectList(wrapper);
    }

}
